/* Includes ------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>

#include "fcntl.h"
#include "fcntlConf.h"
/* typedef -------------------------------------------------------------------*/
/* define --------------------------------------------------------------------*/
#define FILE_ID_OFFSET	3
/* macro ---------------------------------------------------------------------*/
#define	FD(fd) (fd - FILE_ID_OFFSET)
/* variables -----------------------------------------------------------------*/
extern int Fcntl_Lock(void);
extern int Fcntl_Unlock(void);
/* function prototypes -------------------------------------------------------*/
struct File *listFile[FCNTL_FILE_LIST_COUNT];
char *File_GetCompability(const char *path);
int flegal(int fd);
/* functions -----------------------------------------------------------------*/
/*********************************************************************************************

 * file open

**********************************************************************************************/
int open(const char *path, int oflag, ... )
{
    static const struct FcntlDriver FcntlDriverHead __attribute__((used, __section__("file.driver.0"))) = {0};
    static const struct FcntlDriver FcntlDriverTail __attribute__((used, __section__("file.driver.2"))) = {0};
    struct FcntlDriver *driver;

    int result, i = 0;
    char *compatibility = NULL;

    /* find a empty file space */
    for(i=0; i<FCNTL_FILE_LIST_COUNT; i++)
    {   
        if(listFile[i] == NULL)
        {	break;	}
    }
    if(i == FCNTL_FILE_LIST_COUNT)
    {   
        result = -24;           // EMFILE
        goto __end;
    }

    /* alloc memory to store file obj */
    struct File *file = (struct File *)malloc(sizeof(struct File));
    if(file == NULL)
    {   
        result = -12;           // EMFILE
        goto __end;
    }
    memset(file, 0, sizeof(struct File));
	memcpy(file->obj.path, path, strlen(path));
    
    /* get the file driver compatibility from path */
    compatibility =  File_GetCompability(path);
    if(compatibility == NULL)
    {
        result = -6;            // ENXIO
        goto __end;
    }

    /* match the file driver */
    for(driver = (struct FcntlDriver *)&FcntlDriverHead + 1; driver < (struct FcntlDriver *)&FcntlDriverTail; driver++)
    {
        if(strcasecmp(driver->name, compatibility) == 0)
        {
            file->driver = (struct FcntlDriver *)driver;
            break;
        }
    }
    if(driver == &FcntlDriverTail)
    {
        result = -6;            // ENXIO
        goto __end;
    }
    va_list args;
    va_start(args, oflag);
	Fcntl_Lock();
    result = file->driver->ops->open(&file->obj, oflag, args);
	Fcntl_Unlock();
	va_end(args);
    if(result != 0)
    {   goto __end; }
    /* match the file driver */
    
	listFile[i] = file;
    result = i;
	if(result >= 0)
	{	
		result += FILE_ID_OFFSET;	
		return result;
	}
__end:
    if(file != NULL)
    {   free(file); }

    return result;
}
/*********************************************************************************************

 * file write

**********************************************************************************************/
size_t write(int fd, void *buf, size_t count)
{
	size_t countWritten = 0;
	if(flegal(fd) < 0)
	{	return -2;	}       // ENOENT
	
	Fcntl_Lock();
    countWritten = listFile[FD(fd)]->driver->ops->write(&(listFile[FD(fd)]->obj), buf, count);
	Fcntl_Unlock();
	
	return countWritten;
}
/*********************************************************************************************

 * file write

**********************************************************************************************/
size_t read(int fd, void *buf, size_t count)
{
	size_t countRead = 0;
	
	if(flegal(fd) < 0)
	{	return -2;	}       // ENOENT
	
	Fcntl_Lock();
    countRead = listFile[FD(fd)]->driver->ops->read(&(listFile[FD(fd)]->obj), buf, count);
	Fcntl_Unlock();
	
	return countRead;
}
/*********************************************************************************************

 * lseek

**********************************************************************************************/
int lseek(int fd, int offset, int whence)
{
	int result = 0;
	
	if(flegal(fd) < 0)
	{	return -2;	}       // ENOENT
	
	Fcntl_Lock();
	result = listFile[FD(fd)]->driver->ops->lseek(&(listFile[FD(fd)]->obj), offset, whence);
	Fcntl_Unlock();
	
	return result;
}
/*********************************************************************************************

 * file write

**********************************************************************************************/
int close(int fd)
{
	if(flegal(fd) < 0)
	{	return -2;	}       // ENOENT
	
    int result = 0;
	Fcntl_Lock();
    result = listFile[FD(fd)]->driver->ops->close(&(listFile[FD(fd)]->obj)); 
	Fcntl_Unlock();
	
    free(listFile[FD(fd)]);
    listFile[FD(fd)] = NULL;

    return result;
}
/*********************************************************************************************

 * file status

**********************************************************************************************/
int fstat(int fd, struct stat *status)
{
	int result = 0;
	
	if(flegal(fd) < 0)
	{	return -2;	}       // ENOENT
	
	Fcntl_Lock();
    result = listFile[FD(fd)]->driver->ops->fstat(&(listFile[FD(fd)]->obj), status);
	Fcntl_Unlock();	
	
	return result;
}
/*********************************************************************************************

 * file legal check

**********************************************************************************************/
int flegal(int fd)
{
	if(FD(fd) < 0 || FD(fd) >= FCNTL_FILE_LIST_COUNT)
	{	return -2;	}       // ENOENT
	if(listFile[FD(fd)] == NULL)
	{	return -2;}         // ENOENT

	return 0;
}
